package com.fsnbigdata.web;
import com.fsnbigdata.domain.*;
import com.fsnbigdata.domain.dto.NamingPair3;
import com.fsnbigdata.domain.query.OrganismQueryDslBinder;
import com.fsnbigdata.enums.ResultEnum;
import com.fsnbigdata.exception.ParamException;
import com.fsnbigdata.service.api.*;
import com.fsnbigdata.util.ReflectUtil;
import com.fsnbigdata.util.SortTools;
import com.fsnbigdata.vo.Tree;
import com.querydsl.core.types.Predicate;
import io.swagger.annotations.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.querydsl.binding.QuerydslPredicate;
import org.springframework.roo.addon.web.mvc.controller.annotations.ControllerType;
import org.springframework.roo.addon.web.mvc.controller.annotations.RooController;
import org.springframework.roo.addon.web.mvc.controller.annotations.responses.json.RooJSON;
import io.springlets.data.domain.GlobalSearch;

import java.lang.reflect.Method;
import java.security.Principal;
import java.util.*;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import org.springframework.web.util.UriComponents;
import springfox.documentation.annotations.ApiIgnore;

/**
 * = OrganismsCollectionJsonController
 *
 * TODO Auto-generated class documentation
 *
 */
@RooController(entity = Organism.class, pathPrefix = "/api", type = ControllerType.COLLECTION)
@RooJSON
@RestController
@RequestMapping(value = "/api/organisms", name = "OrganismsCollectionJsonController", produces = MediaType.APPLICATION_JSON_VALUE)
@Api(tags = "Organism", description = "生物" )
public class OrganismsCollectionJsonController {

    /**
     * TODO Auto-generated attribute documentation
     *
     */
    private OrganismService organismService;

    private DocumentService documentService;

    @Autowired
    private DistributionService distributionService;
    
    @Autowired
    private TaxonomyService taxonomyService;

    /**
     * TODO Auto-generated constructor documentation
     *
     * @param organismService
     */
    @Autowired
    public OrganismsCollectionJsonController(OrganismService organismService, DocumentService documentService) {
        this.organismService = organismService;
        this.documentService = documentService;
    }

    /**
     * TODO Auto-generated method documentation
     *
     * @return OrganismService
     */
    public OrganismService getOrganismService() {
        return organismService;
    }

    /**
     * TODO Auto-generated method documentation
     *
     * @param organismService
     */
    public void setOrganismService(OrganismService organismService) {
        this.organismService = organismService;
    }

    public DocumentService getDocumentService() {
        return documentService;
    }

    public void setDocumentService(DocumentService documentService) {
        this.documentService = documentService;
    }

    @GetMapping(name = "index", value = "index")
    public ResponseEntity<List<NamingPair3>> category(
        @ApiParam @RequestParam(required = false) String initial) {
        List<NamingPair3> namingPairs = getOrganismService().findAllByInitial(initial);
        return ResponseEntity.ok(namingPairs);
    }

    /*** 自定义搜索
     * @param
     * @param
     * @param pageable
     * @return
     */
    @GetMapping(name = "search", value = "customSearch")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", defaultValue = "0", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "size", defaultValue = "20", dataType = "int", paramType = "query")
    })
    public List getList(HttpServletRequest request, Pageable pageable){
        String name = request.getParameter("name");//关键字
        String content = request.getParameter("content");//desc或asc
        String value = request.getParameter("value");//字段名称
        String latinName = request.getParameter("latinName");//拉丁文
        String kingdom = request.getParameter( "kingdom" );//界
        String family = request.getParameter( "family" );//科
        String genus = request.getParameter( "genus" );//属
        String country = request.getParameter( "country" );//国
        String province = request.getParameter( "province" );//省
        String city = request.getParameter( "city" );//城市

        List organisms= null;
        if (StringUtils.isNotEmpty(content)&&StringUtils.isNotEmpty(value)){
            Pageable pageable1=new PageRequest(pageable.getPageNumber(),pageable.getPageSize(), SortTools.basicSort(content,value));
            organisms=getOrganismService().findByPageAndSize(latinName,kingdom,family,genus,country,province,city,name,pageable1);
        }else{
            organisms=getOrganismService().findByPageAndSize(latinName,kingdom,family,genus,country,province,city,name,pageable);
        }
        return organisms;
    }
    /**
     * 根据省返回市区的物种
     * @return
     */
    @GetMapping(name = "city", value = "getCity")
    public List getCity(HttpServletRequest request){
        List<Tree> trees = new ArrayList<>();
        Distribution distribution = new Distribution();
        String city = request.getParameter( "city" );
        String county= request.getParameter( "county" );
        String kingdom = request.getParameter("kingdom");  //界
        String phylumn = request.getParameter("phylumn");  //门
        String tClass = request.getParameter("tClass");  //纲
        String tOrder = request.getParameter("tOrder");  //目
        String family = request.getParameter("family");  //科
        return organismService.getTree(city,county,kingdom,phylumn,tClass,tOrder,family);
    }

    /**
     * 获取市下面的物种
     * @param request
     * @param pageable
     * @return
     */
    @GetMapping("getPageList")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", defaultValue = "0", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "size", defaultValue = "20", dataType = "int", paramType = "query")
    })
    public List getPageList(HttpServletRequest request,Pageable pageable){
        return organismService.getPageList(request.getParameter( "city" ),pageable);
    }

    @GetMapping(value = "getOne")
    public Taxonomy getOne(HttpServletRequest request){
        return taxonomyService.getByOrganismId( Long.parseLong( request.getParameter( "id" ) ) );
    }
    
    /**
     * 查询出指定区域物种的分类信息
     * @param city
     * @param county
     * @return
     */
    @GetMapping(name = "getOrganismId",value = "getByOrganismId")
    public List<Taxonomy> getOrganismId(String city,String county){
        List<?> list = distributionService.findByCityAndCounty(city,county);
        Iterator iterator = list.iterator();
        List<Long> list1 = new ArrayList<Long>();
        List<Taxonomy> list2 = new ArrayList<Taxonomy>();
       while (iterator.hasNext()){
           list1.add(Long.valueOf(iterator.next().toString()));
       }
       for (int i = 0; i < list1.size(); i++){
           Taxonomy taxonomy = null;
           taxonomy = taxonomyService.findByOrganismId(list1.get(i));
           if (taxonomy != null){
               list2.add(taxonomy);
           }
           
       }
       if (list2 == null|| list2.equals("")){
           return null;
       }
        return list2;
    }


    /**
     * TODO Auto-generated method documentation
     *
     * @param query
     * @param pageable
     * @return ResponseEntity
     */
    @GetMapping(name = "search", value = "search")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", defaultValue = "0", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "size", defaultValue = "20", dataType = "int", paramType = "query")
    })
    public ResponseEntity<Page<Organism>> search(
            @ApiParam @RequestParam(required = false) String query,
            Pageable pageable) {
        GlobalSearch globalSearch = StringUtils.isNotBlank(query) ? new GlobalSearch(query, false) : null;
        Page<Organism> organisms = getOrganismService().findAll(globalSearch, pageable);
        return ResponseEntity.ok(organisms);
    }

    /**
     *
     * @param predicate
     * @param pageable
     * @return
     */
    @GetMapping(name = "search", value = "advanceSearch")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", defaultValue = "0", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "size", defaultValue = "20", dataType = "int", paramType = "query")
    })
    public ResponseEntity<Page<Organism>> advanceSearch(
            @ApiParam @QuerydslPredicate(root = Organism.class, bindings = OrganismQueryDslBinder.class) Predicate predicate,
            Pageable pageable) {
        Page<Organism> organisms = getOrganismService().search(predicate, pageable);
        return ResponseEntity.ok(organisms);
    }

    /**
     * TODO Auto-generated method documentation
     *
     * @param query
     * @param pageable
     * @return ResponseEntity
     */
    @GetMapping(name = "list")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", defaultValue = "0", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "size", defaultValue = "20", dataType = "int", paramType = "query")
    })
    @PreAuthorize("hasRole('ROLE_DB_ORGANISM')")
    public ResponseEntity<Page<Organism>> list(
            Authentication authentication,
            @ApiParam @RequestParam(required = false) String query,
            Pageable pageable) {
        GlobalSearch globalSearch = StringUtils.isNotBlank(query) ? new GlobalSearch(query, false) : null;
        Page<Organism> organisms = null;
        List<String> authorities = authentication.getAuthorities().stream().map(it -> it.getAuthority()).collect(Collectors.toList());
        if(!authorities.contains("ROLE_USER_ADMIN")){
            organisms = getOrganismService().findAll(globalSearch, pageable, authentication.getName());
        }else{
            organisms = getOrganismService().findAll(globalSearch, pageable);
        }

        return ResponseEntity.ok(organisms);
    }

    /**
     * TODO Auto-generated method documentation
     *
     * @return UriComponents
     */
    public static UriComponents listURI() {
        return MvcUriComponentsBuilder.fromMethodCall(MvcUriComponentsBuilder.on(OrganismsCollectionJsonController.class).list(null, null, null)).build().encode();
    }

    /**
     * TODO Auto-generated method documentation
     *
     * @param organism
     * @param result
     * @return ResponseEntity
     */
    @PostMapping(name = "create")
    @PreAuthorize("hasRole('DB_ORGANISM')")
    @ApiOperation(value = "",notes = "save")
    public ResponseEntity<?> create(
            @Valid @RequestBody final Organism organism,
            BindingResult result,
            Principal principal) throws Exception {

        if (organism.getId() != null || organism.getVersion() != null) {
            return ResponseEntity.status(HttpStatus.CONFLICT).build();
        }
        if (result.hasErrors()) {
            throw new ParamException(ResultEnum.PARAM_ERROR.getCode(),
                    result.getFieldError().getDefaultMessage());
        }

        organism.setCreator(principal.getName());

        Set<Document> toSaveDocs = organism.getDocs();
        organism.setDocs(new HashSet<>());

        Organism newOrganism = getOrganismService().save(organism);

        Set<Long> docIds = toSaveDocs.stream().map(doc -> {
            if(doc.getId() == null) {
                doc.setCreator(principal.getName());
                getDocumentService().save(doc);
                return doc.getId();
            }else{
                return doc.getId();
            }
        }).collect(Collectors.toSet());

        getOrganismService().setDocs(newOrganism, docIds);

        Class<?> clazz = OrganismsCollectionJsonController.class;
        Method method = clazz.getMethod("create", ReflectUtil.getPeremeterClass(clazz, "create"));
        ReflectUtil.setAnnotationOn(method,"新增物种库，中文名："+newOrganism.getName());

        //UriComponents showURI = OrganismsItemJsonController.showURI(newOrganism);
        return ResponseEntity.ok(newOrganism);
    }

    /**
     * TODO Auto-generated method documentation
     *
     * @param organisms
     * @param result
     * @return ResponseEntity
     */
    @PostMapping(value = "/batch", name = "createBatch")
    @PreAuthorize("hasRole('DB_ORGANISM')")
    @ApiIgnore
    public ResponseEntity<?> createBatch(@Valid @RequestBody Collection<Organism> organisms, BindingResult result) {
        if (result.hasErrors()) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body(result);
        }

        organisms.forEach(organism -> organism.getTaxonomy().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getSpecimens().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getImages().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getVideos().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getDistribution().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getDocuments().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getConservationStatus().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getGermplasm().forEach(it -> it.setOrganism(organism)));

        getOrganismService().save(organisms);
        return ResponseEntity.created(listURI().toUri()).build();
    }

    /**
     * TODO Auto-generated method documentation
     *
     * @param organisms
     * @param result
     * @return ResponseEntity
     */
    @PutMapping(value = "/batch", name = "updateBatch")
    @PreAuthorize("hasRole('DB_ORGANISM')")
    @ApiIgnore
    public ResponseEntity<?> updateBatch(@Valid @RequestBody Collection<Organism> organisms, BindingResult result) {
        if (result.hasErrors()) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body(result);
        }

        organisms.forEach(organism -> organism.getTaxonomy().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getSpecimens().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getImages().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getVideos().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getDistribution().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getDocuments().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getConservationStatus().forEach(it -> it.setOrganism(organism)));
        organisms.forEach(organism -> organism.getGermplasm().forEach(it -> it.setOrganism(organism)));

        getOrganismService().save(organisms);
        return ResponseEntity.ok().build();
    }

    /**
     * TODO Auto-generated method documentation
     *
     * @param ids
     * @return ResponseEntity
     */
    @DeleteMapping(value = "/batch/{ids}", name = "deleteBatch")
    @PreAuthorize("hasRole('DB_ORGANISM')")
    @ApiIgnore
    public ResponseEntity<?> deleteBatch(@PathVariable("ids") Collection<Long> ids) {
        //TODO will be failed for now
        getOrganismService().delete(ids);
        return ResponseEntity.ok().build();
    }

    /**
     * 根据物种id查询物种信息
     */

    @GetMapping(name = "find" , value="findByOrganismId")
    public Organism findByOrganismId(String organismId){
        return getOrganismService().findOne(Long.parseLong(organismId));
    }

    /**
     * 统计维管、脊索、节肢数量
     */
    @GetMapping("getCountByPhylumn" )
    public List getCountByPhylumn(HttpServletRequest request){
        return getOrganismService().getCountByPhylumn(request.getParameter("phylumn"));
    }

    /**
     * 统计家养数量
     */
    @GetMapping("getCountByuseValue" )
    public List getCountByuseValue(HttpServletRequest request){
        return getOrganismService().getCountByUseValue(request.getParameter("useValue"));
    }

    /**
     * 根据市区和县级市、科、属查询物种信息
     */
    @GetMapping(name = "search", value = "getOrganismAndTaxonomyByCityAndCountry")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", defaultValue = "0", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "size", defaultValue = "20", dataType = "int", paramType = "query")
    })
    public List getOrganismAndTaxonomyByCityAndCountry(HttpServletRequest request,Pageable pageable){
        String city=request.getParameter("city");
        String country =request.getParameter("county");
        String kingdom = request.getParameter("kingdom");  //界
        String phylumn = request.getParameter("phylumn");  //门
        String tClass = request.getParameter("tClass");  //纲
        String tOrder = request.getParameter("tOrder");  //目
        String family = request.getParameter("family");  //科
        String genus = request.getParameter("genus");  //属
        return getOrganismService().getOrganismAndTaxonomyByCityAndCountry(city,country,kingdom,phylumn,tClass,tOrder,family,genus,pageable);

    }

}
